package com.hunter.jerrymouse.request;

import com.hunter.jerrymouse.annotation.request.Param;
import com.hunter.jerrymouse.annotation.request.RequestRouter;
import com.hunter.jerrymouse.annotation.response.ResponseData;
import com.hunter.jerrymouse.response.Response;
import com.hunter.jerrymouse.util.SpringBeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by hunter
 * <p>
 * 请求处理器上下文
 */
@Component
public class RequestHandleContext {

    private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandleContext.class);

    /**
     * 参数解析器
     */
    private static final ParameterNameDiscoverer DISCOVERER = new DefaultParameterNameDiscoverer();

    /**
     * 路径分隔符
     */
    private static final String PATH_SEPARATOR = "/";

    /**
     * 请求句柄池
     */
    private final Map<String, RequestHandle> requestHandleHolder = new ConcurrentHashMap<>();

    /**
     * 刷新处理handle上下文
     */
    public void refreshRequestContext() {
        // 拿到所有注解了request注解的类
        Map<String, Object> handleMap = SpringBeanUtils.getApplicationContext().getBeansWithAnnotation(Controller.class);

        for (Map.Entry<String, Object> handleEntry : handleMap.entrySet()) {

            Class<?> requestClass = handleEntry.getValue().getClass();
            String prefixUrl = "";
            RequestRouter.Method[] classSupportMethod = null;

            // 如果类上注解了@RequestRouter
            RequestRouter requestRouter = requestClass.getAnnotation(RequestRouter.class);
            if (requestRouter != null) {
                // 检查填充uri前缀
                prefixUrl = checkAndFillingPath(requestRouter.path());
                classSupportMethod = requestRouter.method();
            }

            for (Method method : requestClass.getDeclaredMethods()) {
                method.setAccessible(true);
                // 要注解了@RequestRouter的
                requestRouter = method.getAnnotation(RequestRouter.class);
                if (requestRouter == null) {
                    continue;
                }

                // 封装成handle对象
                RequestHandle requestHandle = new RequestHandle();
                // 处理类
                requestHandle.setClazz(requestClass);
                // 处理类的方法
                requestHandle.setMethod(method);
                // 处理类的方法参数信息
                requestHandle.setRequestParameters(this.getMethodParameters(method));
                // 获取handle支持的请求方法, 优先以方法上的注解为准
                RequestRouter.Method[] supportMethod = requestRouter.method();
                if (supportMethod.length == 0 && classSupportMethod != null && classSupportMethod.length > 0) {
                    requestHandle.setSupportHttpMethod(classSupportMethod);
                } else {
                    requestHandle.setSupportHttpMethod(supportMethod);
                }
                // 获取响应体格式
                ResponseData response = method.getAnnotation(ResponseData.class);
                if (response != null) {
                    requestHandle.setResponseContentType(response.contentType());
                } else {
                    requestHandle.setResponseContentType(ResponseData.ContentType.DEFAULT);
                }


                // 检查填充uri
                String requestPath = checkAndFillingPath(requestRouter.path());

                // 重复了
                if (requestHandleHolder.putIfAbsent(prefixUrl + requestPath, requestHandle) != null) {
                    // TODO 暂时用runtime异常处理
                    throw new RuntimeException("Exist router path : " + prefixUrl + requestPath);
                }

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Context loaded handle, uri : {}, handle : {}", prefixUrl + requestPath, requestHandle);
                }
            }
        }
    }

    /**
     * 解析对应的参数信息转为自己的封装类
     *
     * @param handleMethod
     * @return
     */
    private RequestParameter[] getMethodParameters(Method handleMethod) {
        // 反射拿到的参数列表
        Parameter[] nativeParameters = handleMethod.getParameters();
        int paramsLength = nativeParameters.length;
        if (paramsLength == 0) {
            return null;
        }

        // DISCOVERER拿到参数在方法体中名称, 通过反射拿到的是arg1 arg2 arg3
        String[] paramNames = DISCOVERER.getParameterNames(handleMethod);
        RequestParameter[] parameters = new RequestParameter[paramsLength];
        for (int i = 0; i < paramsLength; i++) {
            // 信息装箱
            Parameter nativeParameter = nativeParameters[i];
            RequestParameter parameter = new RequestParameter();
            parameter.setClassType(nativeParameter.getType());

            // 如果是填充ctx和request属性, 后续过程就免去, 即注解在这两个参数上的东西都无效
            if (Response.class.equals(parameter.getClassType())
                    || Request.class.equals(parameter.getClassType())) {
                parameters[i] = parameter;
                continue;
            }

            // 获取参数名, 优先使用@Param中的名
            Param param = nativeParameter.getAnnotation(Param.class);
            if (param != null && param.name().length() > 0) {
                parameter.setName(param.name());
            } else {
                parameter.setName(paramNames[i]);
            }
            parameter.setAnnotations(nativeParameter.getAnnotations());

            parameters[i] = parameter;
        }

        return parameters;
    }

    /**
     * 检查填充路径
     *
     * @param path
     * @return
     */
    private String checkAndFillingPath(String path) {
        if (StringUtils.isEmpty(path)) {
            return PATH_SEPARATOR;
        } else if (!path.startsWith(PATH_SEPARATOR)) {
            return PATH_SEPARATOR + path;
        }

        return path;
    }

    /**
     * 获取请求处理器
     *
     * @return
     */
    public RequestHandle getRequestHandle(String uri) {
        return requestHandleHolder.get(uri);
    }
}
